#include "UsesHandle.h"
#include "spa_types.h"




UsesHandle::UsesHandle(void) {}

UsesHandle::~UsesHandle(void) {}


bool UsesHandle::MergeMultiVector(vector<vector<int>> &head, const vector<vector<int>> tail) 
{
	unsigned int i=0;
	for(; i<head.size(); i++)
		head[i].insert(head[i].end(), tail[i].begin(), tail[i].end());
	
	return true;
}


vector<vector<int>> UsesHandle::FetchValidUsesStatementVar(vector<int> stmts_in, vector<int> var_in) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	for (auto iter_s=stmts_in.begin(); iter_s!=stmts_in.end(); iter_s++) {
		for( auto iter_v=var_in.begin(); iter_v!=var_in.end(); iter_v++) {

			STMT temp;
			temp.statement_number= *iter_s;
			if(PKB::uses.GetStatementUses(temp, *iter_v)) {
				left.push_back(*iter_s);
				right.push_back(*iter_v);
			}
		}
	}
		
	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
}

vector<vector<int>> UsesHandle::FetchValidUsesProcedureVar(vector<int> procs_in, vector<int> var_in)
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	for (auto iter_s=procs_in.begin(); iter_s!=procs_in.end(); iter_s++) {
		for( auto iter_v=var_in.begin(); iter_v!=var_in.end(); iter_v++) {

			if(PKB::uses.GetProcedureUses(*iter_s, *iter_v)) {
				left.push_back(*iter_s);
				right.push_back(*iter_v);
			}
		}
	}
		
	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
	
}

// Statements : While, If, Assign
vector<vector<int>> UsesHandle::GetStatementUsesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));


	vector<vector<int>> sub_result, result;

	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			sub_result = this->UsesVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			sub_result = this->UsesStatementsGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
	
}


vector<vector<int>> UsesHandle::GetAssignmentUsesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each given statement, fetch corresponding used var
			sub_result = this->UsesVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each given var, fetch corresponding assignments that use it
			sub_result = this->UsesAssignmentsGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
	
}

vector<vector<int>> UsesHandle::GetIfUsesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each ifstmt, fetch vars that it uses
			sub_result = this->UsesVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var given, fetch ifstmts that uses it
			sub_result = this->UsesIfGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
}


vector<vector<int>> UsesHandle::GetWhileUsesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each while-stmt, fetch vars that it uses
			sub_result = this->UsesVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var given, fetch wihle-stmts that uses it
			sub_result = this->UsesWhileGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;	
}


vector<vector<int>> UsesHandle::GetContainerUsesVar(vector<int> stmts_in, vector<int> vars_in)
{
	
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each container statement, fetch vars that it uses
			sub_result = this->UsesVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch containers that uses it
			sub_result = this->UsesContainerGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
}


vector<vector<int>> UsesHandle::GetProcedureUsesVar(vector<int> proc_in, vector<int> vars_in)
{
	assert (((proc_in.size() > 0) && vars_in.empty())
		|| (proc_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (proc_in.size() > 0) {
		for (auto iter = proc_in.begin(); iter != proc_in.end(); iter++) {
			// For each procedure, fetch vars that it uses
			sub_result = this->UsesVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch procedures that uses it
			sub_result = this->UsesProcGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
	
}



vector<vector<int>> UsesHandle::UsesStatementsGivenVar(const VARINDEX var_index)
{
	vector<vector<int>> sub_result_set_container, sub_result_set_assign, result_set;
	
	// Fetch containers that modify given var
	sub_result_set_container = UsesContainerGivenVar(var_index);
	sub_result_set_assign = UsesAssignmentsGivenVar(var_index);

	// Merge
	this->MergeMultiVector(result_set, sub_result_set_container);
	this->MergeMultiVector(result_set, sub_result_set_assign);

	return result_set;


}


vector<vector<int>> UsesHandle::UsesAssignmentsGivenVar(const VARINDEX var_index)
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	STMT_ARRAY stmts;


	stmts = PKB::uses.GetAssignmentUsesVariable(var_index);

	// Populate result set

	for (auto iter = stmts.begin(); iter!=stmts.end(); iter++) {
		left.push_back((*iter).statement_number);
		right.push_back(var_index);
	}
	
	result_set.push_back(left);
	result_set.push_back(right);
	
	return result_set;
}

vector<vector<int>> UsesHandle::UsesIfGivenVar(const VARINDEX var_index)
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	STMT_ARRAY if_stmts;
	
	// Fetch if-stmts that use var_name
	if_stmts = PKB::modifies.GetContainerModifiesVariable(var_index, IF);

	// Populate result set 
	for (auto iter = if_stmts.begin(); iter!=if_stmts.end(); iter++) {
		left.push_back((*iter).statement_number);
		right.push_back(var_index);
	}
	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
}


vector<vector<int>> UsesHandle::UsesWhileGivenVar(const VARINDEX var_index)
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	STMT_ARRAY while_stmts;
	
	// Fetch if-stmts that use var_name
	while_stmts = PKB::modifies.GetContainerModifiesVariable(var_index, WHILE);

	// Populate result set : we only need populate LHS
	for (auto iter = while_stmts.begin(); iter!=while_stmts.end(); iter++) {
		left.push_back((*iter).statement_number);
		right.push_back(var_index);
	}
	result_set.push_back(left);
	return result_set;
}

vector<vector<int>> UsesHandle::UsesContainerGivenVar(const VARINDEX var_index)
{

	vector<vector<int>> result_set, sub_result_set_if, sub_result_set_while;

	// Fetch results for if and while statements
	sub_result_set_if = UsesIfGivenVar(var_index);
	sub_result_set_while = UsesWhileGivenVar(var_index);

	// Merge both sets of results
	this->MergeMultiVector(result_set,  sub_result_set_if);
	this->MergeMultiVector(result_set, sub_result_set_while);
	
	return result_set;
}
/*

vector<vector<int>> UsesHandle::UsesStatementsAndVar()
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	INTEGER_ARRAY statements = pkb->GetAllStatement();
	NodeType type;
	STMT temp;
	VARINDEX_ARRAY var_array;

	for (auto iter=statements.begin(); iter!=statements.end(); iter++) {
		temp.statement_number = *iter;
		type = pkb->GetStatementType(*iter);
		switch(type) {
		case ASSIGN: {
			temp.node_type = ASSIGN;
			var_array = pkb->GetVariablesUsedByAssignment(temp);
			break; 
					 }
		case IF: { 
			temp.node_type = IF;
			var_array = pkb->GetVariablesModifiedByContainer(temp);			
			break;			
				 }
		case WHILE: {
			temp.node_type = WHILE;
			var_array = pkb->GetVariablesModifiedByContainer(temp);
			break;
					}
		}
		for (auto iter_p = var_array.begin(); iter!=var_array.end() ;  iter++) {
			left.push_back(*iter);
			right.push_back(*iter_p);
		}
	}

	result_set.push_back(left);
	result_set.push_back(right);
	return result_set;

}*/
/*
vector<vector<int>> UsesHandle::UsesAssignmentsAndVar()
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	VARINDEX_ARRAY var_array;
	STMT temp;
	temp.node_type = ASSIGN;
	INTEGER_ARRAY assignments = pkb->GetAllAssign();
	

	for (auto iter = assignments.begin(); iter!=assignments.end(); iter++) {
		temp.statement_number = *iter;
		var_array = pkb->GetVariablesUsedByAssignment(temp);

		// For each assign statement, find variables that it uses and
		// populate result set
		for (auto iter_p = var_array.begin(); iter_p!=var_array.end(); iter_p++) {
			left.push_back(*iter);
			right.push_back(*iter_p);
		}
	}

	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
}*/
/*
vector<vector<int>> UsesHandle::UsesIfAndVar()
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	VARINDEX_ARRAY var_array;
	STMT temp;
	temp.node_type = IF;
	INTEGER_ARRAY if_stmts = pkb->GetAllIf();

	for (auto iter = if_stmts.begin(); iter!=if_stmts.end(); iter++) {
		temp.statement_number = *iter;
		var_array = pkb->GetVariablesUsedByContainer(temp);

		// For each container statement, find variables that it uses and
		// populate result set
		for (auto iter_p = var_array.begin(); iter_p!=var_array.end(); iter++) {
			left.push_back(*iter);
			right.push_back(*iter_p);
		}
	}
	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
}
*/
/*vector<vector<int>> UsesHandle::UsesWhileAndVar()
{
	vector<vector<int>> result_set;
	VARINDEX_ARRAY var_array;
	STMT temp;
	temp.node_type = WHILE;
	INTEGER_ARRAY if_stmts = pkb->GetAllWhile();

	for (auto iter = if_stmts.begin(); iter!=if_stmts.end(); iter++) {
		temp.statement_number = *iter;
		var_array = pkb->GetVariablesUsedByContainer(temp);

		// For each container statement, find variables that it uses and
		// populate result set
		for (auto iter_p = var_array.begin(); iter_p!=var_array.end(); iter++) {
			result_set[0].push_back(*iter);
			result_set[1].push_back(*iter_p);
		}
	}

	return result_set;
}

vector<vector<int>> UsesHandle::UsesContainerAndVar()
{
	vector<vector<int>> result_set, sub_result_set_if, sub_result_set_while;
	sub_result_set_if = UsesIfAndVar();
	sub_result_set_while = UsesWhileAndVar();
	merge (sub_result_set_if.begin(), sub_result_set_if.end(),
		sub_result_set_while.begin(), sub_result_set_while.end(),
		result_set.begin());

	return result_set;

}*/


vector<vector<int>> UsesHandle::UsesVarsGivenStatement(const int stmt_num)
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	VARINDEX_ARRAY var_array;
	STMT temp;
	temp.statement_number = stmt_num;
	NodeType type = PKB::shortcutTable.GetStatementType(stmt_num);

	switch(type) {
	case IF:
		temp.node_type = IF;
		var_array = PKB::uses.GetVariablesUsedByContainer(temp);
		break;
	case WHILE:
		temp.node_type = WHILE;
		var_array = PKB::uses.GetVariablesUsedByContainer(temp);
		break;
	case ASSIGN:
		temp.node_type = ASSIGN;
		var_array = PKB::uses.GetVariablesUsedByAssignment(temp);
		break;

	}

	for (auto iter = var_array.begin(); iter!=var_array.end() ;  iter++) {
			left.push_back(stmt_num);
			right.push_back(*iter);
	}

	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;


}

/*
vector<vector<int>> UsesHandle::UsesProcAndVars()
{
	PROCINDEX_ARRAY proc_array = pkb->GetAllProcIndex();
	VARINDEX_ARRAY var_array;
	vector<vector<int>> result_set;
	for_each(proc_array.begin(), proc_array.end(), 
		[this, &result_set, &var_array](PROCINDEX proc) {

		var_array = PKB::uses.GetVariablesUsedByProcedure(proc);
		for (auto iter = var_array.begin(); iter != var_array.end(); iter++) {
			result_set[0].push_back(proc);
			result_set[1].push_back(*iter);
		}
	});
	
	return result_set;
}*/

vector<vector<int>> UsesHandle::UsesVarsGivenProc(const PROCINDEX proc_index)
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	// Fetch variables Uses by given Proc
	VARINDEX_ARRAY var_array = PKB::uses.GetVariablesUsedByProcedure(proc_index);

	// Populate result_set
	for (auto iter = var_array.begin(); iter!=var_array.end();  iter++) {
		left.push_back(proc_index);
		right.push_back(*iter);
	}

	result_set.push_back(left);
	result_set.push_back(right);
	
	return result_set;
}

vector<vector<int>> UsesHandle::UsesProcGivenVar(const VARINDEX var_index)
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	// Fetch corresponding var_index of given var_name

	PROCINDEX_ARRAY proc_array;

	proc_array = PKB::uses.GetProcedureUsesVariable(var_index);

	for (auto iter=proc_array.begin(); iter!=proc_array.end(); iter++) {
		left.push_back(*iter);
		right.push_back(var_index);
	}

	result_set.push_back(left);
	result_set.push_back(right);
	return result_set;
}
















